<?php

namespace fast;


class Snowflake
{
    private $epoch; // 起始时间戳
    private $nodeIdBits; // 节点ID的位数
    private $sequenceBits; // 序列号的位数
    private $nodeId; // 节点ID
    private $sequence; // 序列号
    private $lastTimestamp; // 上一次时间戳

    function __construct($nodeId, $epoch = 1420041600000, $nodeIdBits = 10, $sequenceBits = 12)
    {
        $this->epoch = $epoch;
        $this->nodeIdBits = $nodeIdBits;
        $this->sequenceBits = $sequenceBits;
        $this->nodeId = $nodeId;
        $this->sequence = 0;
        $this->lastTimestamp = 0;
    }

    private function getTimestamp()
    {
        return round(microtime(true) * 1000);
    }

    public function nextId()
    {
        $timestamp = $this->getTimestamp();
        if ($timestamp < $this->lastTimestamp) {
            throw new Exception('Current timestamp is smaller than last timestamp.');
        }

        if ($this->lastTimestamp == $timestamp) {
            $this->sequence = ($this->sequence + 1) & ((1 << $this->sequenceBits) - 1);
            if (0 == $this->sequence) {
                $timestamp = $this->tilNextMillis($this->lastTimestamp);
            }
        } else {
            $this->sequence = 0;
        }

        $this->lastTimestamp = $timestamp;

        return (($timestamp - $this->epoch) << ($this->sequenceBits + $this->nodeIdBits)) |
            ($this->nodeId << $this->sequenceBits) |
            $this->sequence;
    }

    private function tilNextMillis($lastTimestamp)
    {
        $timestamp = $this->getTimestamp();
        while ($timestamp <= $lastTimestamp) {
            usleep(1);
            $timestamp = $this->getTimestamp();
        }
        return $timestamp;
    }
}

